//
// This program is free software: you can redistribute it and/or modify
// it under the terms of the GNU Lesser General Public License as published by
// the Free Software Foundation, either version 3 of the License, or
// (at your option) any later version.
// 
// This program is distributed in the hope that it will be useful,
// but WITHOUT ANY WARRANTY; without even the implied warranty of
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
// GNU Lesser General Public License for more details.
// 
// You should have received a copy of the GNU Lesser General Public License
// along with this program.  If not, see http://www.gnu.org/licenses/.
// 

#include "Radio.h"

#include <assert.h>
#include <cmath>

#include "message_m.h"
#include "message_k.h"
#include "info.h"
#include "host_manager.h"

Define_Module(Radio)
;

void Radio::initialize()
{
	// TODO - Generated method body
	id = par("radioId");
	bitErrorRateThresh = par("bitErrorRateThresh").doubleValue();
	timeSlotExpireMsg = new cMessage(NULL, RadioTimeSlotExpireMsgKind);
	minSimuTimeSlot = par("radioMinSimuTimeSlot");
	sendDataBufferTop = 0;
	receiveDataBufferTop = 0;
	scheduleAt(minSimuTimeSlot, timeSlotExpireMsg);
	receiverId = -1;
	HostManager::getInstance()->AddHostRadio(id, this);
	isFirst = false;
	WATCH(sendDataBufferTop);
	WATCH(receiveDataBufferTop);
}

Radio::~Radio()
{
	cancelAndDelete(timeSlotExpireMsg);
}

void Radio::handleMessage(cMessage *msg)
{
	EV<< "Radio Received Message" << endl;
	switch(msg->getKind())
	{
		case RadioTimeSlotExpireMsgKind:
		{
			// send from self
			if(sendDataBufferTop < 0)
			{
				sendDataBufferTop = 0;
			}

			if(sendDataBufferTop != 0)
			{
				RequestChannelInfoMsg *rscMsg = new RequestChannelInfoMsg(NULL,RequestChannelInfoMsgKind);
				rscMsg->setNeedSend(true);
				assert(receiverId >= 0);
				rscMsg->setSenderId(id);
				rscMsg->setReceiverId(receiverId);
				send(rscMsg, "upperControlPort$o");
			}
			else
			{
				RequestSendDataMsg *rsdMsg = new RequestSendDataMsg(NULL, RequestSendDataMsgKind);
				send(rsdMsg, "upperControlPort$o");
			}
			scheduleAt(simTime() + minSimuTimeSlot, timeSlotExpireMsg);
		}
		break;
		// send from upper mac layer
		case MACAssignedDataMsgKind:
		{
			MACAssignedDataMsg *madMsg = check_and_cast<MACAssignedDataMsg *>(msg);
			if(madMsg->getNeedSend())
			{
				sendDataBufferTop = madMsg->getDataPacketSize();
				sendDataTimeLimit = madMsg->getDataSendingTime();
				receiverId = madMsg->getReceiverId();
				isFirst = true;

				assert(timeSlotExpireMsg->isScheduled());
				cancelEvent(timeSlotExpireMsg);
				scheduleAt(simTime(), timeSlotExpireMsg);
			}
			else
			{
				RequestChannelInfoMsg *rscMsg = new RequestChannelInfoMsg(NULL,RequestChannelInfoMsgKind);
				rscMsg->setNeedSend(false);
				rscMsg->setReceiverId(-1);
				rscMsg->setSenderId(-1);
				receiverId = -1;
				send(rscMsg, "upperControlPort$o");

				RequestChannelPhysicalInfoMsg *rcpiMsg = new RequestChannelPhysicalInfoMsg(NULL, RequestChannelPhysicalInfoMsgKind);
				rcpiMsg->setNeedSend(false);
				rcpiMsg->setReceiverId(-1);
				rcpiMsg->setSenderId(-1);
				send(rcpiMsg, "coordinatorControlPort$o");
			}
			delete msg;
		}
		break;
		case ChannelInfoMsgKind:
		{
			ChannelInfoMsg *ciMsg = check_and_cast<ChannelInfoMsg *>(msg);

			assert(ciMsg->getSenderId() == id);
			assert(ciMsg->getReceiverId() == receiverId);

			ChannelInfo channelInfo;
			ciMsg->setChannelInfo(channelInfo);
			RequestChannelPhysicalInfoMsg* rcpiMsg = new RequestChannelPhysicalInfoMsg(NULL, RequestChannelPhysicalInfoMsgKind);
			rcpiMsg->setChannelInfo(ciMsg->getChannelInfo());
			rcpiMsg->setSenderId(ciMsg->getSenderId());
			rcpiMsg->setReceiverId(ciMsg->getReceiverId());
			rcpiMsg->setNeedSend(true);
			send(rcpiMsg, "coordinatorControlPort$o");
			delete msg;

			break;
		}
		break;
		case ChannelPhysicalInfoMsgKind:
		{
			ChannelPhysicalInfoMsg *cpiMsg = check_and_cast<ChannelPhysicalInfoMsg *>(msg);
			ChannelPhysicalInfo channelPhysicalInfo;
			channelPhysicalInfo = cpiMsg->getChannelPhysicalInfo();
			assert(id == cpiMsg->getSenderId());
			assert(receiverId == cpiMsg->getReceiverId());

			cGate* srcGate = gateHalf("dataTransmitPort", cGate::OUTPUT)->getNextGate();
			HostManager *hostManager = HostManager::getInstance();
			Radio *radio = hostManager->getHostRadio(receiverId);
			assert(radio != NULL);
			cGate* destGate = HostManager::getInstance()->getHostRadio(receiverId)->gateHalf("dataTransmitPort", cGate::INPUT)->getPreviousGate();

			cDatarateChannel *channel = cDatarateChannel::create(NULL);
			setDatarateChannel(channelPhysicalInfo, channel);

			srcGate->connectTo(destGate, channel);

			assert(channel->getBitErrorRate() == channelPhysicalInfo.ber);

			DataMsg *dMsg = new DataMsg(NULL, DataMsgKind);
			int dataSize = std::min(static_cast<int>(std::floor(channelPhysicalInfo.dataRate * minSimuTimeSlot.dbl())), sendDataBufferTop);
			dMsg->setBitLength(dataSize);
			dMsg->setIsFirst(isFirst);
			dMsg->setSenderID(id);
			dMsg->setReceiverID(receiverId);
			dMsg->setDataRate(channelPhysicalInfo.dataRate);

			if(dataSize >= sendDataBufferTop)
			{
				dMsg->setIsLast(true);
			}
			send(dMsg, "dataTransmitPort$o");
			isFirst = false;

			delete msg;

			break;
		}
		break;
		case DataMsgKind:
		{
			DataMsg* dMsg = check_and_cast<DataMsg *>(msg);
			cGate* srcGate = dMsg->getSenderGate()->getNextGate();
			srcGate->disconnect();

			if(dMsg->getIsFirst())
			{
				dataTransmitInfo.init();
				dataTransmitInfo.startTime = simTime();
				dataTransmitInfo.senderID = dMsg->getSenderID();
				dataTransmitInfo.receiverID = dMsg->getReceiverID();
				dataTransmitInfo.dataRate = dMsg->getDataRate();
			}
			dataTransmitInfo.totalPacketCount++;
			dataTransmitInfo.totalBitCount += dMsg->getBitLength();

			DataConfirmMsg *dataConfirmMsg = new DataConfirmMsg(NULL, DataConfirmMsgKind);

			if(!dMsg->hasBitError())
			{
				dataConfirmMsg->setSuccessfulFlag(true);
				dataConfirmMsg->setReceivedBitSize(dMsg->getBitLength());

				if(dMsg->getIsLast())
				{
					dataTransmitInfo.endTime = simTime();
					DataTransmitInfoMsg *dataTransmitInfoMsg = new DataTransmitInfoMsg(NULL, DataTransmitInfoMsgKind);
					dataTransmitInfoMsg->setDataTransmitInfo(dataTransmitInfo);
					send(dataTransmitInfoMsg, "upperControlPort$o");
				}
			}
			else
			{
				dataConfirmMsg->setSuccessfulFlag(false);
				dataConfirmMsg->setReceivedBitSize(0);
				dataTransmitInfo.lossPacketCount++;
				dataTransmitInfo.lossBitCount += dMsg->getBitLength();
			}
			cGate *srcControlGate = gateHalf("dataControlPort", cGate::OUTPUT)->getNextGate();
			cGate *destControlGate = msg->getSenderModule()->gateHalf("dataControlPort", cGate::INPUT)->getPreviousGate();

			cChannel *channel = cIdealChannel::create(NULL);
			srcControlGate->connectTo(destControlGate, channel);

			send(dataConfirmMsg, "dataControlPort$o");
		}
		break;
		case DataConfirmMsgKind:
		{
			DataConfirmMsg *dataConfirmMsg = check_and_cast<DataConfirmMsg *>(msg);
			cGate *srcGate = dataConfirmMsg->getSenderGate()->getNextGate();
			srcGate->disconnect();

			if(dataConfirmMsg->getSuccessfulFlag())
			{
				int sendDataLength = dataConfirmMsg->getReceivedBitSize();
				sendDataBufferTop -= sendDataLength;
			}
		}
		break;
		case EndSimuMsgKind:
		{
			cancelEvent(timeSlotExpireMsg);
			DeleteHostMsg *protocolDHMsg = new DeleteHostMsg(NULL, DeleteHostMsgKind);
			send(protocolDHMsg, "upperControlPort$o");
			DeleteHostMsg *coordinatorMsg = new DeleteHostMsg(NULL, DeleteHostMsgKind);
			send(coordinatorMsg, "coordinatorControlPort$o");
		}
		break;
	}
}

void Radio::setDatarateChannel(ChannelPhysicalInfo channelPhysicalInfo, cDatarateChannel *channel)
{
	channel->setBitErrorRate(channelPhysicalInfo.ber);
}
